package xyz.lwm.lazycat.transaction.policy;

import xyz.lwm.lazycat.transaction.execute.Propagation;
import xyz.lwm.lazycat.transaction.execute.TranDefinition;
import xyz.lwm.lazycat.transaction.execute.TransactionException;

import static xyz.lwm.lazycat.transaction.execute.Propagation.MANDATORY;
import static xyz.lwm.lazycat.transaction.execute.Propagation.NESTED;
import static xyz.lwm.lazycat.transaction.execute.Propagation.NEVER;
import static xyz.lwm.lazycat.transaction.execute.Propagation.NOT_SUPPORTED;
import static xyz.lwm.lazycat.transaction.execute.Propagation.REQUIRED;
import static xyz.lwm.lazycat.transaction.execute.Propagation.REQUIRES_NEW;
import static xyz.lwm.lazycat.transaction.execute.Propagation.SUPPORTS;

/**
 * 事务策略
 *
 * @author lwm
 */
public interface TransactionPolicy {

    static TransactionPolicy getPolicy(TranDefinition tranDefinition) {
        TransactionPolicy policy;

        Propagation propagation = tranDefinition.getPropagation();
        if (propagation == REQUIRED) {
            policy = new RequiredPolicy(tranDefinition);

        } else if (propagation == SUPPORTS) {
            policy = new SupportsPolicy(tranDefinition);

        } else if (propagation == MANDATORY) {
            policy = new MandatoryPolicy(tranDefinition);

        } else if (propagation == REQUIRES_NEW) {
            policy = new RequiresNewPolicy(tranDefinition);

        } else if (propagation == NOT_SUPPORTED) {
            policy = new NotSupportedPolicy(tranDefinition);

        } else if (propagation == NEVER) {
            policy = new NeverPolicy(tranDefinition);

        } else if (propagation == NESTED) {
            policy = new NestedPolicy(tranDefinition);

        } else {
            throw new TransactionException("unsupported propagation");
        }
        return policy;
    }

    /**
     * 执行事务
     *
     * @param supplier 业务逻辑
     * @param <T>      业务逻辑返回结果类型
     * @return 业务逻辑返回结果
     * @throws Exception 业务逻辑执行异常
     */
    <T> T execute(CheckedSupplier<T> supplier) throws Throwable;


    interface CheckedSupplier<T> {
        T get() throws Throwable;
    }

    interface CheckedRunnable {
        void run() throws Throwable;
    }

}
